package com.dib.neo.bkfs.fs;

import java.nio.ByteBuffer;
import java.nio.file.attribute.*;
import java.util.concurrent.TimeUnit;

public class BKFileAttributes implements BasicFileAttributes {

  private int mode;
  private int blockSize = 512 * 1024;
  private long ino;
  private int uid;
  private int gid;
  private long size;
  long mtime;
  long ctime;

  public BKFileAttributes() {
  }

  public int getBlockSize() {
    return blockSize;
  }

  public int getMode() {
    return mode;
  }

  public void setMode(int mode) {
    this.mode = mode;
  }

  public long getIno() {
    return ino;
  }

  public int getUid() {
    return uid;
  }

  public int getGid() {
    return gid;
  }


  public BKFileAttributes setBlockSize(int blockSize) {
    this.blockSize = blockSize;
    return this;
  }

  public BKFileAttributes setIno(long ino) {
    this.ino = ino;
    return this;
  }

  public BKFileAttributes setUid(int uid) {
    this.uid = uid;
    return this;
  }

  public BKFileAttributes setGid(int gid) {
    this.gid = gid;
    return this;
  }

  public BKFileAttributes setSize(long size) {
    this.size = size;
    return this;
  }



  public BKFileAttributes regularFile() {
    mode = (mode & ~BKConstants.S_IFMT) | BKConstants.S_IFREG;
    return this;
  }

  public BKFileAttributes directory() {
    mode = (mode & ~BKConstants.S_IFMT) | BKConstants.S_IFDIR;
    return this;
  }

  public BKFileAttributes symbolicLink() {
    mode = (mode & ~BKConstants.S_IFMT) | BKConstants.S_IFLNK;
    return this;
  }

  @Override
  public FileTime lastModifiedTime() {
    return FileTime.from(mtime, TimeUnit.MILLISECONDS);
  }

  @Override
  public FileTime lastAccessTime() {
    return lastModifiedTime();
  }

  @Override
  public FileTime creationTime() {
    return FileTime.from(ctime, TimeUnit.MILLISECONDS);
  }

  @Override
  public boolean isRegularFile() {
    return ((mode & BKConstants.S_IFMT) == BKConstants.S_IFREG);
  }

  @Override
  public boolean isDirectory() {
    return ((mode & BKConstants.S_IFMT) == BKConstants.S_IFDIR);
  }

  @Override
  public boolean isSymbolicLink() {
    return ((mode & BKConstants.S_IFMT) == BKConstants.S_IFLNK);
  }


  @Override
  public boolean isOther() {
    int type = mode & BKConstants.S_IFMT;
    return (type != BKConstants.S_IFREG &&
        type != BKConstants.S_IFDIR &&
        type != BKConstants.S_IFLNK);
  }

  @Override
  public long size() {
    return size;
  }

  @Override
  public Long fileKey() {
    return ino;
  }


  public byte[] data() {
    ByteBuffer buffer = ByteBuffer.allocate(7 * 8);
    buffer.putInt(mode);
    buffer.putInt(blockSize);
    buffer.putLong(ino);
    buffer.putInt(uid);
    buffer.putInt(gid);
    buffer.putLong(size);
    return buffer.array();
  }

  public void read(byte[] data) {
    ByteBuffer buffer = ByteBuffer.wrap(data);
    mode = buffer.getInt();
    blockSize = buffer.getInt();
    ino = buffer.getLong();
    uid = buffer.getInt();
    gid = buffer.getInt();
    size = buffer.getLong();
  }

  // wrap this object with BasicFileAttributes object to prevent leaking of
  // user information
  BasicFileAttributes asBasicFileAttributes() {
    return BKAsBasicFileAttributes.wrap(this);
  }

  // unwrap BasicFileAttributes to get the underlying BKFileAttributes
  // object. Returns null is not wrapped.
  static BKFileAttributes toBKFileAttributes(BasicFileAttributes attrs) {
    if (attrs instanceof BKFileAttributes) {
      return (BKFileAttributes) attrs;
    }
    if (attrs instanceof BKAsBasicFileAttributes) {
      return ((BKAsBasicFileAttributes) attrs).unwrap();
    }
    return null;
  }

  // wrap a BKFileAttributes object as a BasicFileAttributes
  private static class BKAsBasicFileAttributes implements BasicFileAttributes {

    private final BKFileAttributes attrs;

    private BKAsBasicFileAttributes(BKFileAttributes attrs) {
      this.attrs = attrs;
    }

    static BKAsBasicFileAttributes wrap(BKFileAttributes attrs) {
      return new BKAsBasicFileAttributes(attrs);
    }

    BKFileAttributes unwrap() {
      return attrs;
    }

    @Override
    public FileTime lastModifiedTime() {
      return attrs.lastModifiedTime();
    }

    @Override
    public FileTime lastAccessTime() {
      return attrs.lastAccessTime();
    }

    @Override
    public FileTime creationTime() {
      return attrs.creationTime();
    }

    @Override
    public boolean isRegularFile() {
      return attrs.isRegularFile();
    }

    @Override
    public boolean isDirectory() {
      return attrs.isDirectory();
    }

    @Override
    public boolean isSymbolicLink() {
      return attrs.isSymbolicLink();
    }

    @Override
    public boolean isOther() {
      return attrs.isOther();
    }

    @Override
    public long size() {
      return attrs.size();
    }

    @Override
    public Object fileKey() {
      return attrs.fileKey();
    }
  }

}
